gRPC gateway 数据的转换
通常把 gRPC 用作内部通信,而使用 Restful Api 进行外部通信。为了避免写两套应用(或者为了兼容旧服务),我们使用 grpc-gateway 把 gRPC 转成 HTTP。服务接收到 HTTP 请求后,grpc-gateway 把它转成 gRPC 进行处理,然后以 JSON 形式返回数据。
这里使用 gRPC-Gateway 这个开源库,gRPC-Gateway 是一个插件,它为 gRPC 服务生成反向代理服务器,将 Restful/JSON 转换为 gRPC,反之亦然。
换句话说,gRPC-Gateway 将在你的 gRPC 服务上创建一个层,该层将充当客户端的 Restful/JSON 服务。gRPC-Gateway 从 gRPC 服务的 Protocol Buffer 的定义生成代码。
然后使用 go get 获取 grpc-gateway。
首先安装依赖
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
go get -u github.com/golang/protobuf/protoc-gen-go;
工作的流程
1、首先定义 .proto 文件; 2、然后由 protoc 将 .proto 文件编译成 protobuf 格式的数据; 3、将 2 中编译后的数据传递到各个插件,生成对应语言、对应模块的源代码。
Go Plugins 用于生成 .pb.go 文件 gRPC-Gateway 则是 pb.gw.go
项目结构
└── src
└── grpc-helloworld-gateway
├── gateway
│ └── main.go
├── greeter_server
│ └── main.go
└── helloworld
├── helloworld.pb.go
├── helloworld.pb.gw.go
└── helloworld.proto
具体使用方式
安装 protoc 文件
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway@v1.14.5
下载之后就能在 github.com\grpc-ecosystem\grpc-gateway@v1.14.5\third_party\googleapis\google\api
目录下找到对应的 proto 文件了
执行完上述命令后,就会将 protoc-gen-grpc-gateway
下载到电脑的 GOPATH 下,自己电脑的 GOPATH 可以通过命令 go env
查看。
所以对应的就是
$GOPATH\pkg\mod\github.com\grpc-ecosystem\grpc-gateway@v1.14.5\third_party\googleapis
所以引用路径就是
# 注意: 路径中要用 '/'
protoc -I $GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.14.5/third_party/googleapis \
--go_out=plugins=grpc:. ./*.proto
生成对应服务
创建 helloworld.proto 文件,里面引入 google 官方的 api 相关的扩展,为 grpc 的 http 转换提供了支持。
syntax = "proto3";
package helloworld;
import "google/api/annotations.proto";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
这里的 google/api/annotations.proto
是谷歌官方
这里增加了对 http 的扩展配置。
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
然后编译 proto 文件,生成对应的 go 文件
cd src/grpc-helloworld-gateway
# -I 指定 protoc 的搜索 import 的 proto 的文件夹。
protoc -I/usr/local/include -I. \
-I$GOPATH/pkg/mod/ \
-I$GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.14.5/third_party/googleapis \
--go_out=plugins=grpc:. \
helloworld/helloworld.proto
这里生成了 helloworld/helloworld.pb.go
文件。
helloworld.pb.go 是 server 服务需要的,下一步我们需要使用 protoc 生成 gateway 需要的 go 文件。
# 生成 Swagger 文件与 gateway 文件
protoc -I/usr/local/include -I. \
-I$GOPATH/pkg/mod/ \
-I$GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.14.5/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
--swagger_out=logtostderr=true:. \
helloworld/helloworld.proto
编写 Gateway 服务
package main
import (
"flag"
"google.golang.org/grpc/credentials/insecure"
"net/http"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
gw "alsritter.icu/grpc-gateway/helloworld"
)
var (
echoEndpoint = flag.String("echo_endpoint", "localhost:50051", "endpoint of YourService")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := gw.RegisterGreeterHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":8080", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
首先 echoEndpoint 存储了需要连接的 server 信息,然后将这些信息和新建的 server 用 gw.go 中的 RegisterGreeterHandlerFromEndpoint 进行一个注册和绑定
这时底层就会连接 echoEndpoint 提供的远程 server 地址,这样 gateway 就作为客户端和远程 server 建立了连接,之后用 http 启动新建的 server,gateway 就作为服务器端对外提供 http 的服务了。
启动服务后就可以使用 http 去请求了(别忘了关代理)
$ go run greeter_server/main.go
$ curl -X POST -k http://localhost:8080/v1/example/echo -d '{"name": " world"}'
{"message":"Hello world"}
curl 用 post 向 gateway 发送请求,gateway 作为 proxy 将请求转化一下通过 grpc 转发给 greeter_server,然后 greeter_server 通过 grpc 返回结果,gateway 收到结果后,转化成 json 返回给前端。
生成 swagger 文档
由上面的命令,已经生成了 helloworld.swagger.json
可以将其导入任何支持 OpenAPI 规范的平台进行浏览,例如: